package com.spmsys.util;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 异常链类
 */
public class ExceptionStackTrace {

    /**
     * 初始化
     *
     * @param exception 异常
     * @return 初始化信息
     */
    private static @NotNull List<Map<String, String>> init(@NotNull Exception exception) {
        try (StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter)) {
            exception.printStackTrace(printWriter);
            List<Map<String, String>> stackTraces = new ArrayList<>();
            for (String item : stringWriter.toString().split("Caused by: ")) {
                Map<String, String> stackTrace = new HashMap<>();
                int atIndex = item.indexOf("\tat");
                int exceptionIndex = item.indexOf("Exception");
                stackTrace.put("exceptionType", (exceptionIndex != -1 && exceptionIndex + 9 <= item.length()) ? item.substring(0, exceptionIndex + 9) : null);
                stackTrace.put("exceptionInformation", (exceptionIndex != -1 && atIndex != -1 && exceptionIndex + 11 < atIndex - 2) ? item.substring(exceptionIndex + 11, atIndex - 2) : null);
                stackTrace.put("callChain", atIndex != -1 ? item.substring(atIndex).replace("\t", "") : null);
                stackTraces.add(stackTrace);
            }
            return stackTraces;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取异常链长度方法
     *
     * @param e 异常
     * @return 异常链长度
     */
    public static @NotNull Integer length(Exception e) {
        List<Map<String, String>> stackTraces = init(e);
        return stackTraces.size();
    }

    /**
     * 获取异常链方法
     *
     * @param e 异常
     * @return 异常链
     */
    public static @NotNull List<Map<String, String>> getStackTraces(Exception e) {
        return init(e);
    }

    /**
     * 获取指定异常方法
     *
     * @param e     异常
     * @param index 索引
     * @return 异常
     */
    public static @Nullable Map<String, String> getStackTrace(Exception e, Integer index) {
        List<Map<String, String>> stackTraces = init(e);
        return index >= 0 && index < stackTraces.size() ? stackTraces.get(index) : null;
    }

    /**
     * 获取异常类型方法
     *
     * @param e     异常
     * @param index 索引
     * @return 异常类型
     */
    public static @Nullable String getExceptionType(Exception e, Integer index) {
        List<Map<String, String>> stackTraces = init(e);
        return index >= 0 && index < stackTraces.size() ? stackTraces.get(index).get("exceptionType") : null;
    }

    /**
     * 获取异常信息方法
     *
     * @param e     异常
     * @param index 索引
     * @return 异常信息
     */
    public static @Nullable String getExceptionInformation(Exception e, Integer index) {
        List<Map<String, String>> stackTraces = init(e);
        return index >= 0 && index < stackTraces.size() ? stackTraces.get(index).get("exceptionInformation") : null;
    }

    /**
     * 获取异常调用链方法
     *
     * @param e     异常
     * @param index 索引
     * @return 异常调用链
     */
    public static @Nullable String getCallChain(Exception e, Integer index) {
        List<Map<String, String>> stackTraces = init(e);
        return index >= 0 && index < stackTraces.size() ? stackTraces.get(index).get("callChain") : null;
    }
}
